﻿using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;

using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.Logging;

using zijian666.WorkScheduler.Abstractions;
using zijian666.WorkScheduler.Defaults;
using zijian666.WorkScheduler.Defines;

namespace zijian666.WorkScheduler;

public static class WorkSchedulerExtensions
{
    /// <summary>
    /// 
    /// </summary>
    /// <param name="cts"></param>
    /// <returns></returns>
    internal static CancellationToken GetToken(this CancellationTokenSource cts)
    {
        if (cts.IsCancellationRequested)
        {
            return new CancellationToken(true);
        }
        return cts.Token;
    }

    /// <summary>
    /// 已取消
    /// </summary>
    /// <param name="cts"></param>
    /// <param name="message"></param>
    /// <exception cref="OperationCanceledException">已取消</exception>
    internal static void ThrowIfCancellationRequested(this CancellationTokenSource cts, string? message = null)
    {
        if (cts.IsCancellationRequested)
        {
            throw new OperationCanceledException(message);
        }
    }

    /// <summary>
    /// 如果对象实现了 <seealso cref="IDisposable"/>接口则尝试释放对象, 且忽略异常
    /// </summary>
    internal static void TryDispose(this object? obj)
    {
        try
        {
            (obj as IDisposable)?.Dispose();
        }
        catch (Exception e)
        {
            Trace.WriteLine(e);
        }
    }

    /// <summary>
    /// 如果 <paramref name="condition"/>为 <see langword="true" /> 则抛出异常
    /// </summary>
    /// <exception cref="ObjectDisposedException"></exception>
    internal static void ThrowIfDisposed(this object? obj,
#if NET6_0_OR_GREATER
[DoesNotReturnIf(true)]
#endif
    bool condition, string? message = null)
    {
        if (condition)
        {
            throw new ObjectDisposedException(message);
        }
    }

    /// <summary>
    /// 如果 <paramref name="obj"/> 为  <see langword="null" /> 则抛出异常
    /// </summary>
    /// <param name="obj"></param>
    /// <param name="name"></param>
    /// <exception cref="ArgumentNullException"></exception>
    internal static void ThrowIfNull(this object? obj, string name)
    {
        if (obj is null)
        {
            throw new ArgumentNullException(name);
        }
    }

    internal static void ThrowIfNullOrWhiteSpace(this string? str, string name)
    {
        if (string.IsNullOrWhiteSpace(str))
        {
            throw new ArgumentNullException(name);
        }
    }

    public static IServiceCollection AddWorkScheduler<TManager>(this IServiceCollection services, string name, Action<WorkOptions>? configure)
        where TManager : class, IWorkManager
    {
        services.ThrowIfNull(nameof(services));
        name.ThrowIfNullOrWhiteSpace(nameof(name));

        services.TryAddSingleton<WorkScheduler>();
        services.AddSingleton<WorkSchedulerSettings>(new WorkSchedulerSettings
        {
            Name = name,
            Configure = configure,
        });

        services.TryAddScoped<IWorkService, WorkService>();
        services.TryAddScoped<IWorkManager, TManager>();
        services.TryAddScoped<IWorkDataBuffer, WorkDataBuffer>();
        services.TryAddScoped<WorkOptions>(p =>
        {
            var name = p.GetService<WorkScheduler>()?.GetScopeName(p);
            if (name is null)
            {
                return null;
            }
            var opt = new WorkOptions();
            foreach (var s in p.GetServices<WorkSchedulerSettings>())
            {
                if (s.Name is null || s.Name.Equals(name, StringComparison.OrdinalIgnoreCase))
                {
                    s.Configure?.Invoke(opt);
                }
            }
            return opt;
        });


        return services;
    }

    public static IWorkService GetWorkService(this IServiceProvider services, string name)
    {
        return services.GetRequiredService<WorkScheduler>().GetWorkService(name)
            ?? throw new InvalidOperationException($"未找到名称为 {name} 的工作服务。");
    }

    public static string GetName(this IWorkService workService)
        => workService.GetRequiredService<WorkScheduler>().GetScopeName(workService);
}